﻿using Mapster;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Shipeng.Util;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Yitter.IdGenerator;
using YNTK.Common.Const;
using YNTK.Common.Enum;
using YNTK.Common.Filter;
using YNTK.Common.Util;
using YNTK.Dependency;
using YNTK.DynamicApiController;
using YNTK.FriendlyException;
using YNTK.System.Entitys.Dto.Common;
using YNTK.System.Entitys.Dto.Menu;
using YNTK.System.Entitys.Dto.Permission.Role;
using YNTK.System.Entitys.Role;
using YNTK.System.Interfaces.Permission;
using YNTK.System.Interfaces.Resources;

namespace YNTK.System.Service.Permission
{
    /// <summary>
    /// 角色服务的实现
    /// 版 本：V1.0.0
    /// 版 权：云南通科科技有限公司（https://www.yntkkj.com）
    /// 作 者：李仕鹏
    /// 日 期：2022-10-04 
    /// </summary>
    [ApiDescriptionSettings(Tag = "角色管理", Name = "TkRole", Order = 794)]
    [Route("api/[controller]")]
    public class TkRoleService : ITkRoleService, IDynamicApiController, ITransient
    {
        private readonly ISqlSugarRepository<TkRoleEntity> _tkRoleRepository;//角色仓储
        private readonly ISqlSugarRepository<TkRoleMenuRelevancyEntity> _tkRoleMenuRelevancyRepository;//角色菜单关联仓储       
        private readonly HttpContext _httpContext;
        private readonly IOrganizeService _organizeService;//组织服务
        private readonly IDictionaryService _dictionaryService;//字典服务
        private readonly IMenuService _menuService;//菜单服务

        /// <summary>
        /// 构造方法依赖注入
        /// </summary>
        /// <param name="tkRoleRepository">角色仓储</param>
        /// <param name="tkRoleMenuRelevancyRepository">角色菜单关联仓储</param>
        /// <param name="organizeService">组织服务</param>
        /// <param name="dictionaryService">字典服务</param>
        /// <param name="menuService">菜单服务</param>
        public TkRoleService(ISqlSugarRepository<TkRoleEntity> tkRoleRepository,
            ISqlSugarRepository<TkRoleMenuRelevancyEntity> tkRoleMenuRelevancyRepository,
            IOrganizeService organizeService, IDictionaryService dictionaryService,
            IMenuService menuService)
        {
            _tkRoleRepository = tkRoleRepository;
            _tkRoleMenuRelevancyRepository = tkRoleMenuRelevancyRepository;
            _organizeService = organizeService;
            _dictionaryService = dictionaryService;
            _menuService = menuService;
            _httpContext = App.HttpContext;
        }

        /// <summary>
        /// 分页获取角色数据
        /// </summary>
        /// <param name="query">查询角色请求模型</param>
        /// <returns></returns>
        [HttpPost("gets")]
        public async Task<TkPageResult<TkRoleListDto>> Gets([FromBody] QueryTkRoleFilterRequestModel query)
        {
            try
            {
                var user = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID);
                string organizeId = user.Subject.FindFirst(ClaimConst.ORGANIZEID).Value;
                //账号类型【0.平台运营人员 1.超级管理员 2.组织管理员 3.组织用户 4.第三方登录用户 5.游客用户】
                var authenticationType = user.Subject.FindFirst(ClaimConst.ACCOUNTTYPE).Value;
                if(authenticationType=="4" || authenticationType=="5")
                    throw YNTKException.Oh(ErrorCode.D1611);

                var data = await _tkRoleRepository.Entities
                .WhereIF(!string.IsNullOrWhiteSpace(query.dictionaryId), r => r.DictionaryId.Contains(query.dictionaryId))
                .WhereIF(query.enabledMark == 1, r => r.EnabledMark == 1)
                .WhereIF(query.enabledMark == 0, r => r.EnabledMark != 1)
                .WhereIF(!string.IsNullOrWhiteSpace(query.keyword),r=>r.Name.Contains(query.keyword) 
                || r.Code.Contains(query.keyword) || r.Description.Contains(query.keyword))
                .WhereIF(authenticationType == "0" || authenticationType == "1",
                r=> (r.Classify == 0 && r.OrganizeId == organizeId) || (r.Classify == 1 && r.Administrator))
                .WhereIF(authenticationType == "2" || authenticationType == "3",
                r=> r.Classify == 1 && r.OrganizeId == organizeId)
                .WhereIF(!string.IsNullOrWhiteSpace(query.organizeId),r=>r.OrganizeId==query.organizeId)
                .Where(r=>r.DeleteMark==null)
                .OrderBy(r=>r.SortCode, OrderByType.Asc)
                .OrderBy(r => r.CreatorTime, OrderByType.Desc)
                .ToPagedListAsync(query.currentPage, query.pageSize);

                var pageList = new SqlSugarPagedList<TkRoleListDto>()
                {
                    list = data.list.Adapt<List<TkRoleListDto>>(),
                    pagination = data.pagination
                };
                if (pageList.list != null && pageList.list.Count() > 0)
                {
                    var orgIds = pageList.list.Select(r => r.organizeId).Distinct().ToList();
                    var orgs = await _organizeService.GetOrganizeListByIds(orgIds);
                    var dicIds = pageList.list.Where(r=>!string.IsNullOrWhiteSpace(r.dictionaryId))
                     ?.Select(r => r.dictionaryId.Split(',').ToList())
                     .SelectMany(r => r).Distinct().ToList();
                    var dics = await _dictionaryService.GetDictionaryListByIds(dicIds);
                    foreach (var item in pageList.list)
                    {
                        if (!string.IsNullOrWhiteSpace(item.organizeId))
                            item.organizeName = orgs?.FirstOrDefault(r => r.Id == item.organizeId)?.FullName;
                        if (!string.IsNullOrWhiteSpace(item.dictionaryId))
                        {
                            item.dictionaryText = string.Join('/', dics
                                ?.Where(r => item.dictionaryId.Split(',').ToList().Contains(r.Id))
                                ?.Select(r => r.Name)?.ToList());
                        }
                    }
                }
                return TkPageResult<TkRoleListDto>.Success(pageList);
            }
            catch (Exception)
            {
                throw YNTKException.Oh("分页获取角色数据发生异常");
            }
        }

        /// <summary>
        /// 添加角色
        /// </summary>
        /// <param name="input">添加角色请求模型</param>
        /// <returns></returns>
        [HttpPost("add")]
        public async Task Add([FromBody] AddTkRoleRequestModel input)
        {
            var user = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID);
            string organizeId = user.Subject.FindFirst(ClaimConst.ORGANIZEID).Value;
            //账号类型【0.平台运营人员 1.超级管理员 2.组织管理员 3.组织用户 4.第三方登录用户 5.游客用户】
            var authenticationType = user.Subject.FindFirst(ClaimConst.ACCOUNTTYPE).Value;
            if (authenticationType == "4" || authenticationType == "5")
                throw YNTKException.Oh(ErrorCode.D1611);          

            var entity = input.Adapt<TkRoleEntity>();
            if (authenticationType == "0" || authenticationType == "1")
            {
                //运营平台分配角色         
                entity.Classify = 0;//角色分类(0.运营平台角色 1.组织)
                if (input.organizeId != organizeId)
                {
                    if(await _tkRoleRepository.AnyAsync(r=>r.OrganizeId== input.organizeId && 
                    r.Administrator && r.DeleteMark==null))
                        throw YNTKException.Oh(ErrorCode.D1613);
                    entity.Classify = 1;
                    entity.Administrator = true;//是否为组织管理员角色
                    //组织管理员角色标识,{组织id}#{Administrator}#{角色编号}
                    entity.Identification =$"{input.organizeId}#{entity.Administrator}#{input.code}";
                    entity.OrganizeId = input.organizeId;
                }
            }
            else
            {
                //组织分配角色
                entity.Classify = 1;//角色分类(0.运营平台角色 1.组织)
                entity.Administrator = false;
                entity.Identification = "";
                if(input.organizeId!= organizeId) entity.OrganizeId = organizeId;
            }
            if (await _tkRoleRepository.AnyAsync(r => r.Name == input.name && 
            r.OrganizeId == entity.OrganizeId && r.DeleteMark == null))
                throw YNTKException.Oh(ErrorCode.D1601);
            if (await _tkRoleRepository.AnyAsync(r => r.Code == input.code && 
            r.OrganizeId == entity.OrganizeId && r.DeleteMark == null))
                throw YNTKException.Oh(ErrorCode.D1600);

            var isOk = await _tkRoleRepository.Context.Insertable(entity)
                .CallEntityMethod(m => m.Creator()).ExecuteReturnEntityAsync();
            _ = isOk ?? throw YNTKException.Oh(ErrorCode.D1602);
        }

        /// <summary>
        /// 修改角色
        /// </summary>
        /// <param name="input">修改角色请求模型</param>
        /// <returns></returns>
        [HttpPut("update")]
        public async Task Update([FromBody] UpdateTkRoleRequestModel input)
        {
            var user = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID);
            string organizeId = user.Subject.FindFirst(ClaimConst.ORGANIZEID).Value;
            var authenticationType = user.Subject.FindFirst(ClaimConst.ACCOUNTTYPE).Value;
            //账号类型【0.平台运营人员 1.超级管理员 2.组织管理员 3.组织用户 4.第三方登录用户 5.游客用户】
            if (authenticationType == "4" || authenticationType == "5")
                throw YNTKException.Oh(ErrorCode.D1611);

            var old = await _tkRoleRepository.FirstOrDefaultAsync(r => r.Id == input.id && r.DeleteMark == null);
            if (old==null)throw YNTKException.Oh(ErrorCode.D1608);          

            var entity = input.Adapt<TkRoleEntity>();
            if (authenticationType == "0" || authenticationType == "1")
            {
                //运营平台分配角色         
                entity.Classify = 0;//角色分类(0.运营平台角色 1.组织)
                entity.Administrator = false;//是否为组织管理员角色 
                entity.Identification = ""; //组织管理员角色标识,{组织id}#{Administrator}#{角色编号}
                if (input.organizeId != organizeId)
                {
                    if (await _tkRoleRepository.AnyAsync(r => r.OrganizeId == input.organizeId &&
                   r.Administrator && r.Id!=input.id && r.DeleteMark == null))
                        throw YNTKException.Oh(ErrorCode.D1613);
                    entity.Classify = 1;
                    entity.Administrator = true;                  
                    entity.Identification = $"{input.organizeId}#{entity.Administrator}#{input.code}";
                    entity.OrganizeId = input.organizeId;
                }
            }
            else
            {
                //组织分配角色
                 if(old.Administrator || !string.IsNullOrWhiteSpace(old.Identification))
                    throw YNTKException.Oh(ErrorCode.D1612);
                entity.Classify = 1;//角色分类(0.运营平台角色 1.组织)
                entity.Administrator = false;
                entity.Identification = "";
                if (input.organizeId != organizeId) entity.OrganizeId = organizeId;
            }
            if (await _tkRoleRepository.AnyAsync(r => r.Name == input.name &&
            r.OrganizeId == entity.OrganizeId && r.Id!=input.id && r.DeleteMark == null))
                throw YNTKException.Oh(ErrorCode.D1601);
            if (await _tkRoleRepository.AnyAsync(r => r.Code == input.code &&
            r.OrganizeId == entity.OrganizeId && r.Id != input.id && r.DeleteMark == null))
                throw YNTKException.Oh(ErrorCode.D1600);

            var isOK = await _tkRoleRepository.Context.Updateable(entity)
                .IgnoreColumns(ignoreAllNullColumns: true)
                .CallEntityMethod(m => m.LastModify()).ExecuteCommandAsync();
            if (!(isOK > 0))
                throw YNTKException.Oh(ErrorCode.D1610);
        }

        /// <summary>
        /// 删除角色
        /// </summary>
        /// <param name="id">角色id</param>
        /// <returns></returns>
        [HttpDelete("delete/{id}")]
        public async Task Delete([Required] string id)
        {
            var user = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID);
            var authenticationType = user.Subject.FindFirst(ClaimConst.ACCOUNTTYPE).Value;
            //账号类型【0.平台运营人员 1.超级管理员 2.组织管理员 3.组织用户 4.第三方登录用户 5.游客用户】
            if (authenticationType == "4" || authenticationType == "5")
                throw YNTKException.Oh(ErrorCode.D1611);
            var entity = await _tkRoleRepository.FirstOrDefaultAsync(r => r.Id ==id && r.DeleteMark == null);
            if (entity == null) throw YNTKException.Oh(ErrorCode.D1608);
            if(authenticationType == "0" || authenticationType == "1")
            {

            }
            else
            {
                if (entity.Administrator || !string.IsNullOrWhiteSpace(entity.Identification))
                    throw YNTKException.Oh(ErrorCode.D1614);
            }
            var isOk = await _tkRoleRepository.Context.Updateable(entity)
                .IgnoreColumns(ignoreAllNullColumns: true)
                .CallEntityMethod(m => m.Delete()).ExecuteCommandAsync();
            if (!(isOk > 0))
                throw YNTKException.Oh(ErrorCode.D1609);
        }

        /// <summary>
        /// 更新角色状态(启用/禁用)
        /// </summary>
        /// <param name="id">角色id</param>
        /// <returns></returns>
        [HttpPut("state/{id}")]
        public async Task UpdateState([Required] string id)
        {
            var user = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID);
            var userId = user.Value;
            var authenticationType = user.Subject.FindFirst(ClaimConst.ACCOUNTTYPE).Value;
            //账号类型【0.平台运营人员 1.超级管理员 2.组织管理员 3.组织用户 4.第三方登录用户 5.游客用户】
            if (authenticationType == "4" || authenticationType == "5")
                throw YNTKException.Oh(ErrorCode.D1611);

            var entity = await _tkRoleRepository.FirstOrDefaultAsync(r => r.Id == id && r.DeleteMark == null);
            if (entity == null) throw YNTKException.Oh(ErrorCode.D1608);
            if (authenticationType == "0" || authenticationType == "1")
            {

            }
            else
            {
                if (entity.Administrator || !string.IsNullOrWhiteSpace(entity.Identification))
                    throw YNTKException.Oh(ErrorCode.D1615);
            }
            var isOk = await _tkRoleRepository.Context.Updateable<TkRoleEntity>().SetColumns(it => new TkRoleEntity()
            {
                EnabledMark = SqlFunc.IIF(it.EnabledMark == 1, 0, 1),
                LastModifyUserId = userId,
                LastModifyTime = SqlFunc.GetDate()
            }).Where(it => it.Id == id).ExecuteCommandAsync();
            if (!(isOk > 0))
                throw YNTKException.Oh(ErrorCode.D1616);
        }

        /// <summary>
        /// 设置角色菜单权限
        /// </summary>
        /// <param name="set">设置角色菜单权限请求模型</param>
        /// <returns></returns>
        [HttpPut("setpermissions")]
        public async Task SetPermissions([FromBody] SetRoleMenuPermissionsRequestModel set)
        {
            var user = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID);
            var userId = user.Value;
            var authenticationType = user.Subject.FindFirst(ClaimConst.ACCOUNTTYPE).Value;
            //账号类型【0.平台运营人员 1.超级管理员 2.组织管理员 3.组织用户 4.第三方登录用户 5.游客用户】
            if (authenticationType == "4" || authenticationType == "5")
                throw YNTKException.Oh(ErrorCode.D1611);
            var role = await _tkRoleRepository.FirstOrDefaultAsync(r => r.Id == set.id && r.DeleteMark == null);
            if (role == null) throw YNTKException.Oh(ErrorCode.D1608);
            if (authenticationType == "2" || authenticationType == "3")
            {
                if (role.Administrator || !string.IsNullOrWhiteSpace(role.Identification))
                    throw YNTKException.Oh(ErrorCode.D1617);
            }
            try
            {
                //开启事务
                _tkRoleMenuRelevancyRepository.Ado.BeginTran();
                var roleModelList = new List<TkRoleMenuRelevancyEntity>();
                set.menuData.ForEach(item =>
                {
                    var entity = new TkRoleMenuRelevancyEntity();
                    entity.Id= YitIdHelper.NextId().ToString();
                    entity.TkRoleId = set.id;
                    entity.MenuId = item.menuId;
                    entity.Authoritys = item.authoritys;
                    roleModelList.Add(entity);
                });
                await _tkRoleMenuRelevancyRepository.DeleteAsync(r=>r.TkRoleId==set.id);
                await _tkRoleMenuRelevancyRepository.Context.Insertable(roleModelList)
                    .ExecuteCommandAsync();
                _tkRoleMenuRelevancyRepository.Ado.CommitTran();
            }
            catch (Exception)
            {
                _tkRoleMenuRelevancyRepository.Ado.RollbackTran();
                throw YNTKException.Oh(ErrorCode.D1618);
            }
        }

        /// <summary>
        /// 获取角色菜单权限数据
        /// </summary>
        /// <param name="id">角色id</param>
        /// <returns></returns>
        [HttpGet("getpermissions/{id}")]
        public async Task<TkResult<List<RoleMenuPermissionsListDto>>> Get([Required] string id)
        {
            var data = await _tkRoleMenuRelevancyRepository.Entities
                .Where(r => r.TkRoleId == id).ToListAsync();
            var result = data.Adapt<List<RoleMenuPermissionsListDto>>();
            if(result!=null && result.Count > 0)
            {
                var permissions = _menuService.GetAuthorityList().data;
                result.FindAll(r => !string.IsNullOrWhiteSpace(r.authoritys))
                        .ForEach(item => {
                            item.authoritysData =(from it in permissions
                            .Where(p => item.authoritys.Split(',').ToList().Contains(p.Value.ToStr()))
                            .ToList()
                            select new EnumListDto() {Id=item.menuId,Value=it.Value,Description=it.Description}
                            ).ToList();
                            item.authoritysText = string.Join(",",
                                item.authoritysData.Select(r => r.Description).ToArray());
                        });
            }
            return TkResult<List<RoleMenuPermissionsListDto>>.Success(result);
        }


        #region PublicMethod
        /// <summary>
        /// 根据角色id集合获取角色数据
        /// </summary>
        /// <param name="ids">角色id集合</param>
        /// <returns></returns>
        [NonAction]
        public async Task<List<TkRoleEntity>> GetTkRoleListByIdsAsync(List<string> ids)
        {
            if (ids == null || ids.Count <= 0) return null;
            var result = await _tkRoleRepository.Entities
                .Where(r => ids.Contains(r.Id) && r.DeleteMark == null).ToListAsync();
            return result;
        }

        /// <summary>
        /// 获取组织管理员角色
        /// </summary>
        /// <returns></returns>
        [NonAction]
        public async Task<TkRoleEntity> GetOrganizePermissionsAsync()
        {
            var user = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID);
            string organizeId = user.Subject.FindFirst(ClaimConst.ORGANIZEID).Value;
            var entity = await _tkRoleRepository.FirstOrDefaultAsync(r=>r.OrganizeId== organizeId && 
            r.Administrator && !string.IsNullOrWhiteSpace(r.Identification) && r.DeleteMark==null);
            return entity;
        }

        /// <summary>
        /// 根据角色id获取菜单树机构数据
        /// </summary>
        /// <param name="id">角色id</param>
        /// <returns></returns>
        [NonAction]
        public async Task<List<MenuTreeDto>> GetMenuTreeByRoleId(string id)
        {
            var permissions = (await Get(id)).data;
            if (permissions == null || permissions.Count <= 0) return null;
            List<string> menuIds = permissions.Select(r => r.menuId).Distinct().ToList();
            var menuData = await _menuService.GetListAsync(menuIds);
            menuData = menuData?.Where(m => m.EnabledMark == 1)?.ToList();
            if (menuData == null || menuData.Count <= 0) return null;
            var treeList = menuData.Adapt<List<MenuTreeDto>>();
            RoleMenuPermissionsListDto dto =null;
            treeList.FindAll(r => !string.IsNullOrWhiteSpace(r.authoritys))
                       .ForEach(item => {
                           dto = permissions.FirstOrDefault(r=>r.menuId==item.id);
                           item.authoritys = dto.authoritys;
                           item.authoritysData = dto.authoritysData;
                           item.authoritysText = dto.authoritysText;
                       });
            treeList = treeList.ToTree().OrderBy(r => r.sortCode).ToList();
            return treeList;
        }
        #endregion

    }
}
